package demo.monitor.opentelemetry;


import io.opentelemetry.api.GlobalOpenTelemetry;
import io.opentelemetry.api.OpenTelemetry;
import io.opentelemetry.api.common.AttributeKey;
import io.opentelemetry.api.common.Attributes;
import io.opentelemetry.api.metrics.*;
import io.opentelemetry.api.trace.Tracer;
import io.opentelemetry.context.Context;
import io.opentelemetry.context.ContextKey;
import org.apache.poi.sl.usermodel.ObjectMetaData;
import org.junit.Test;

import java.util.Random;
import java.util.concurrent.atomic.AtomicReference;


public class OpenTelemetryMetricDemo {


    private static final ContextKey<String> SHAPE_CONTEXT_KEY = ContextKey.named("shape");
    private static final Random random = new Random();

    static final AttributeKey<String> WIDGET_SHAPE = AttributeKey.stringKey("com.acme.widget.shape");
    static final AttributeKey<String> WIDGET_COLOR = AttributeKey.stringKey("com.acme.widget.color");

    static final Attributes WIDGET_RED_SQUARE =
            Attributes.of(WIDGET_SHAPE, "square", WIDGET_COLOR, "red");
    static final Attributes WIDGET_RED_CIRCLE =
            Attributes.of(WIDGET_SHAPE, "circle", WIDGET_COLOR, "circle");

    static String computeWidgetShape() {
        return random.nextBoolean() ? "circle" : "square";
    }

    static String computeWidgetColor() {
        return random.nextBoolean() ? "red" : "blue";
    }

    static Context customContext() {
        return Context.current().with(SHAPE_CONTEXT_KEY, computeWidgetShape());
    }

    @Test
    public void testOpenTelemetry() {
        OpenTelemetry openTelemetry = GlobalOpenTelemetry.get();
        Tracer tracer = openTelemetry.getTracer(ObjectMetaData.Application.class.getName());
        Meter meter = openTelemetry.getMeter("demo.monitor.opentelemetry.OpenTelemetryMetricDemo");
        LongHistogram doWorkHistogram = meter.histogramBuilder("do-work").ofLongs().build();
        AttributeKey<String> ATTR_METHOD = AttributeKey.stringKey("method");
        doWorkHistogram.record(100, Attributes.of(ATTR_METHOD, "ping"));
    }


    public static void counterUsage(Meter meter) {
        // Construct a counter to record measurements that are always positive (monotonically
        // increasing).
        LongCounter counter = meter
                .counterBuilder("fully.qualified.counter")
                .setDescription("A count of produced widgets")
                .setUnit("{widget}")
                // optionally change the type to double
                // .ofDoubles()
                .build();

        // Record a measurement with no attributes or context.
        // Attributes defaults to Attributes.empty(), context to Context.current().
        counter.add(1L);

        AttributeKey<String> ATTR_METHOD = AttributeKey.stringKey("method");
        counter.add(1L, Attributes.of(ATTR_METHOD, "GET"));

    }


    public static void asyncGaugeUsage(Meter meter) {
        AtomicReference<Double> processingLineTemp = new AtomicReference<>(273.0);

        // Construct an async counter to observe an existing counter in a callback
        ObservableDoubleGauge asyncGauge = meter
                        .gaugeBuilder("fully.qualified.gauge")
                        .setDescription("The current temperature of the widget processing line")
                        .setUnit("K")
                        // Uncomment to optionally change the type to long
                        // .ofLongs()
                        .buildWithCallback(
                                // the callback is invoked when a MetricReader reads metrics
                                observableMeasurement -> {
                                    double currentWidgetCount = processingLineTemp.get();

                                    // Record a measurement with no attributes.
                                    // Attributes defaults to Attributes.empty().
                                    observableMeasurement.record(currentWidgetCount);

                                    // Record a measurement with attributes, using pre-allocated attributes whenever
                                    // possible.
                                    observableMeasurement.record(currentWidgetCount, WIDGET_RED_CIRCLE);
                                    // Sometimes, attributes must be computed using application context.
                                    observableMeasurement.record(
                                            currentWidgetCount,
                                            Attributes.of(
                                                    WIDGET_SHAPE, computeWidgetShape(), WIDGET_COLOR, computeWidgetColor()));
                                });

        // Optionally close the gauge to unregister the callback when required
        asyncGauge.close();
    }


    /**
     * 直方图，包含 DoubleHistogram 和 LongHistogram。
     * 它会把测量值分到不同区间（桶）中，记录每个区间的测量值数量、所有测量值总和以及总计数。借助这些数据，能近似计算出 TP 值。
     * @param meter
     */
    public static void histogramUsage(Meter meter) {
        // Construct a histogram to record measurements where the distribution is important.
        DoubleHistogram histogram =
                meter.histogramBuilder("fully.qualified.histogram")
                        .setDescription("Length of time to process a widget")
                        .setUnit("s")
                        // Uncomment to optionally provide advice on useful default explicit bucket boundaries
                        // .setExplicitBucketBoundariesAdvice(Arrays.asList(1.0, 2.0, 3.0))
                        // Uncomment to optionally change the type to long
                        // .ofLongs()
                        .build();

        Attributes WIDGET_RED_CIRCLE = Attributes.of(WIDGET_SHAPE, "circle", WIDGET_COLOR, "circle");

        // Record a measurement with no attributes or context.
        // Attributes defaults to Attributes.empty(), context to Context.current().
        histogram.record(1.1);

        // Record a measurement with attributes, using pre-allocated attributes whenever possible.
        histogram.record(2.2, WIDGET_RED_CIRCLE);
        // Sometimes, attributes must be computed using application context.
        histogram.record(
                3.2, Attributes.of(WIDGET_SHAPE, computeWidgetShape(), WIDGET_COLOR, computeWidgetColor()));

        // Record a measurement with attributes, and context.
        // Most users will opt to omit the context argument, preferring the default Context.current().
        histogram.record(4.4, WIDGET_RED_CIRCLE, customContext());
    }

}
